import logging
import re
from datetime import datetime, timedelta, date

from json_repair import json_repair

from core.models import WeeklyReportTimeLimit

logger = logging.getLogger(__name__)


def cst2utc(cst_time: str | datetime) -> datetime:
    """
    Convert the China Standard Time (CST) to a UTC time string.

    Parameters:
        cst_time: The China Standard Time string, in the format of "YYYY-MM-DD" or "YYYY-MM-DD HH:MM:SS"

    Returns:
        The UTC time string, in the format of "YYYY-MM-DD HH:MM:SS"
    """
    if isinstance(cst_time, datetime):
        return cst_time - timedelta(hours=8)  # Convert UTC to CST (CST is UTC+8)
    elif isinstance(cst_time, str):
        cst_time = cst_time.strip()
        if len(cst_time) == 10:
            cst_time = datetime.strptime(cst_time, "%Y-%m-%d")
        elif len(cst_time) == 19:
            cst_time = datetime.strptime(cst_time, "%Y-%m-%d %H:%M:%S")
        else:
            raise ValueError("Invalid CST time format. Use 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'.")

        utc_time = cst_time - timedelta(hours=8)  # Convert CST to UTC (CST is UTC+8)
        return utc_time
    else:
        raise TypeError("cst_time must be a string or datetime object.")


def utc2cst(utc_time: str | datetime) -> datetime:
    """
    Convert the UTC time string to a China Standard Time (CST) string.

    Parameters:
        utc_time: The UTC time string, in the format of "YYYY-MM-DD" or "YYYY-MM-DD HH:MM:SS"

    Returns:
        The CST time string, in the format of "YYYY-MM-DD HH:MM:SS"
    """
    if isinstance(utc_time, datetime):
        return utc_time + timedelta(hours=8)  # Convert UTC to CST (CST is UTC+8)
    elif isinstance(utc_time, str):
        utc_time = utc_time.strip()
        if len(utc_time) == 10:
            utc_time = datetime.strptime(utc_time, "%Y-%m-%d")
        elif len(utc_time) == 19:
            utc_time = datetime.strptime(utc_time, "%Y-%m-%d %H:%M:%S")
        else:
            raise ValueError("Invalid UTC time format. Use 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'.")
        cst_time = utc_time + timedelta(hours=8)  # Convert UTC to CST (CST is UTC+8)
        return cst_time
    else:
        raise TypeError("utc_time must be a string or datetime object.")


def time2timestamp(time_obj: str | datetime) -> int:
    """
    Convert a time string or datetime object to a Unix timestamp.

    Parameters:
        time_obj: The time string in the format of "YYYY-MM-DD HH:MM:SS" or a datetime object.

    Returns:
        The Unix timestamp as an integer.
    """
    if isinstance(time_obj, str):
        time_obj = time_obj.strip()
        if len(time_obj) == 10:
            time_obj = datetime.strptime(time_obj, "%Y-%m-%d")
        elif len(time_obj) == 19:
            time_obj = datetime.strptime(time_obj, "%Y-%m-%d %H:%M:%S")
        else:
            raise ValueError("Invalid time format. Use 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'.")

    if isinstance(time_obj, datetime):
        return int(time_obj.timestamp())
    else:
        raise TypeError("time_obj must be a string or datetime object.")


def timestamp2time(timestamp: int | float, time_format: str = "%Y-%m-%d %H:%M:%S") -> str:
    """
    Convert a Unix timestamp to a formatted time string.

    Parameters:
        timestamp: The Unix timestamp as an integer or float.
        time_format: The desired output format, default is "%Y-%m-%d %H:%M:%S".

    Returns:
        The formatted time string.
    """
    dt_obj = datetime.fromtimestamp(timestamp)
    return dt_obj.strftime(time_format)


def time2weekday(time_obj: str | datetime) -> str:
    """
    Convert a time string or datetime object to the corresponding weekday.

    Parameters:
        time_obj: The time string in the format of "YYYY-MM-DD HH:MM:SS" or a datetime object.

    Returns:
        The weekday as a Chinese string, e.g., "周一", "周二", etc.
    """
    if isinstance(time_obj, str):
        time_obj = time_obj.strip()
        if len(time_obj) == 10:
            time_obj = datetime.strptime(time_obj, "%Y-%m-%d")
        elif len(time_obj) == 19:
            time_obj = datetime.strptime(time_obj, "%Y-%m-%d %H:%M:%S")
        else:
            raise ValueError("Invalid time format. Use 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'.")

    weekday_map = {
        0: "周一",
        1: "周二",
        2: "周三",
        3: "周四",
        4: "周五",
        5: "周六",
        6: "周日"
    }
    if isinstance(time_obj, datetime):
        weekday_index = time_obj.weekday()
        return weekday_map.get(weekday_index, "")
    else:
        raise TypeError("time_obj must be a string or datetime object.")


def shift_date(time_obj: str | datetime, weeks: int = 0, to_week_start: bool = False, to_week_end: bool = False) -> str:
    """
    Shift a date string by a specified number of weeks, and optionally adjust the day of the week.
    `to_week_start` and `to_week_end` cannot both be True at the same time.

    Parameters:
        time_obj: The date string in the format of "YYYY-MM-DD" or a datetime object.
        weeks: The number of weeks to shift the date, default is zero, shifts within the current week.
        to_week_start: If True, shift to the first day of the week (Monday).
        to_week_end: If True, shift to the last day of the week (Sunday).

    Returns:
        The new date string after shifting.
    """
    if to_week_start and to_week_end:
        raise ValueError("Cannot set both `to_week_start` and `to_week_end` to True.")
    if isinstance(time_obj, str):
        time_obj = time_obj.strip()
        if len(time_obj) == 10:
            time_obj = datetime.strptime(time_obj, "%Y-%m-%d")
        elif len(time_obj) == 19:
            time_obj = datetime.strptime(time_obj, "%Y-%m-%d %H:%M:%S")
        else:
            raise ValueError("Invalid time format. Use 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'.")

    if to_week_start:
        # Shift to the start of the week (Monday)
        days_to_shift = -time_obj.weekday() + weeks * 7
    elif to_week_end:
        # Shift to the end of the week (Sunday)
        days_to_shift = 6 - time_obj.weekday() + weeks * 7
    else:
        # Just shift by the number of weeks without changing the day of the week
        days_to_shift = weeks * 7
    new_date = time_obj + timedelta(days=days_to_shift)
    return new_date.strftime("%Y-%m-%d")


async def get_range_date(base_datetime: str | datetime | date) -> tuple[str, str, str]:
    """
    Get the start, middle, and end time strings for the weekly report based on the base date.
    Consistent with the time range of the weekly report system.
    start_time: The start time of the current weekly report period.
    middle_time: The end time of the current weekly report period.
    end_time: The end time of the next weekly report period.
    Parameters:
        base_datetime: The base datetime string in the format of "YYYY-MM-DD HH:MM:SS" or "YYYY-MM-DD".
    Returns:
        A tuple containing the start time, middle time, and end time strings in the format of "YYYY-MM-DD HH:MM:SS".
    """
    if isinstance(base_datetime, str):
        base_datetime = base_datetime.strip()
        try:
            base_date = datetime.strptime(base_datetime, "%Y-%m-%d")
        except ValueError:
            try:
                base_date = datetime.strptime(base_datetime, "%Y-%m-%d %H:%M:%S")
            except ValueError:
                raise ValueError("Invalid date format. Use 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'.")
    elif isinstance(base_datetime, datetime):
        base_date = base_datetime
    elif isinstance(base_datetime, date):
        base_date = datetime.combine(base_datetime, datetime.min.time())
    else:
        raise TypeError("base_datetime must be a string or datetime object.")

    this_week_record = await WeeklyReportTimeLimit.filter(end_time_limit__lt=base_datetime).order_by(
        "-work_report_start").first()
    next_week_record = await WeeklyReportTimeLimit.filter(end_time_limit__gt=base_datetime).order_by(
        "work_report_start").first()
    if this_week_record:
        start_time = this_week_record.work_report_start.strftime("%Y-%m-%d %H:%M:%S")
        middle_time = this_week_record.work_report_end.strftime("%Y-%m-%d %H:%M:%S")
    else:
        # 处理周一早上9点前的特殊情况，算作上周的时间范围
        if base_date.weekday() == 0 and base_date.hour < 9:
            start_date = shift_date(base_date, weeks=-1, to_week_start=True)
            middle_date = shift_date(base_date, weeks=-1, to_week_end=True)
        else:
            start_date = shift_date(base_date, weeks=0, to_week_start=True)  # 获取当前周周一的时间
            middle_date = shift_date(base_date, weeks=0, to_week_end=True)  # 获取当前周周日的时间
        start_time = f"{start_date} 00:00:00"
        middle_time = f"{middle_date} 23:59:59"
    if next_week_record:
        end_time = next_week_record.work_report_end.strftime("%Y-%m-%d %H:%M:%S")
    else:
        end_date = shift_date(base_date, weeks=1, to_week_end=True)  # 获取下周周日的时间
        end_time = f"{end_date} 23:59:59"

    return start_time, middle_time, end_time


def parse_json(json_str: str) -> dict:
    """
    Parse a JSON string into a Python dictionary.

    Parameters:
        json_str: The JSON string to parse.

    Returns:
        A dictionary representation of the JSON string.
    """
    match = re.search(r"```(json)?(.*?)```", json_str, re.DOTALL)
    if match:
        json_str = match.group(2)
        result = json_repair.loads(json_str)
        if isinstance(result, dict):
            return result
        elif isinstance(result, list):
            return {"result": result}
        else:
            logger.warning(f"Parsed JSON is not a dictionary or list. {result}")
    else:
        logger.warning(f"Input string is not a valid JSON format. {json_str}")


def get_meeting_content(meeting):
    """获取会议内容，优先级：content_summary_new > content_summary > content"""
    content_summary_new = meeting.get("content_summary_new")
    content_summary = meeting.get("content_summary")
    content = meeting.get("content")
    meeting_summary = meeting.get("meeting_summary")

    if content_summary_new:
        # 如果使用 content_summary_new，且有个人会议纪要总结，则拼接
        if meeting_summary:
            return f"{content_summary_new}\n{meeting_summary}"
        else:
            return content_summary_new
    elif content_summary:
        return content_summary
    else:
        return content
